package org.demo.jetty;

import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.junit.Test;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * Jetty是一个提供 HTP服务器、HTTP客户端和javax.servlet容器的开源项目。
 * Jetty有一个口号：不要把应用部署到Jetty上，要把Jetty部署到你的应用里。
 * 嵌入一个Jetty的典型步骤如下：
 * <ol>
 * <li>创建一个server实例
 * <li>新增/配置连接
 * <li>新增/配置处理程序，或者Contexts，或者Servlets
 * <li>启动Server
 * <li>等待连接或者在当前线程上做一些其他的事
 * <ol/>
 */
public class JettyDemo {

    @Test
    public void testManyConnectors() throws Exception {

        //这个例子会展示如何配置SSL，我们需要一个秘钥库，会在jetty.home下面找
        String jettyDistKeystore = "../../jetty-distribution/target/distribution/demo-base/etc/keystore";
        String keystorePath = System.getProperty("example.keystore", jettyDistKeystore);
        File keystoreFile = new File(keystorePath);
        if (!keystoreFile.exists()) {
            throw new FileNotFoundException(keystoreFile.getAbsolutePath());
        }

        //创建一个不指定端口的Server，随后将直接配置连接和端口
        Server server = new Server();

        //HTTP配置
        //HttpConfiguration是一个配置http和https属性的集合，默认的配置是http的
        //带secured的ui配置https的，
        HttpConfiguration http_config = new HttpConfiguration();
        http_config.setSecureScheme("https");
        http_config.setSecurePort(8443);
        http_config.setOutputBufferSize(32768);

        //HTTP连接
        //第一个创建的连接是http连接，传入刚才创建的配置信息，也可以重新设置新的配置，如端口，超时等
        ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(http_config));
        http.setPort(8080);
        http.setIdleTimeout(30000);

        //使用SslContextFactory来创建http
        //SSL需要一个证书，所以我们配置一个工厂来获得需要的东西
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
        sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
        sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");

        //HTTPS的配置类
        HttpConfiguration https_config = new HttpConfiguration(http_config);
        SecureRequestCustomizer src = new SecureRequestCustomizer();
        src.setStsMaxAge(2000);
        src.setStsIncludeSubDomains(true);
        https_config.addCustomizer(src);

        //HTTPS连接
        //创建第二个连接，
        ServerConnector https = new ServerConnector(server,
                new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
                new HttpConnectionFactory(https_config));
        https.setPort(8443);
        https.setIdleTimeout(500000);

        // 设置一个连接的集合
        server.setConnectors(new Connector[]{http, https});

        // 设置一个处理器
        server.setHandler(new HelloHandler());

        // 启动服务
        server.start();
        server.join();
    }

    @Test
    public void testMyResource() throws Exception {
        Server server = new Server(8080);

        //创建一个ResourceHandler，它处理请求的方式是提供一个资源文件
        //这是一个Jetty内置的处理器，所以它非常适合与其他处理器构成一个处理链
        ResourceHandler resourceHandler = new ResourceHandler();

        //配置ResourceHandler，设置哪个文件应该被提供给请求方
        //这个例子里，配置的是当前路径下的文件，但是实际上可以配置长任何jvm能访问到的地方
        resourceHandler.setDirectoriesListed(true);
        resourceHandler.setWelcomeFiles(new String[]{"index.html"});
        resourceHandler.setResourceBase("src/main/web");

        // 将resource_handler添加到GzipHandler中，然后将GzipHandler提供给Server
        GzipHandler gzip = new GzipHandler();
        server.setHandler(gzip);

        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[]{resourceHandler, new DefaultHandler()});
        server.setHandler(handlers);

        server.start();
        server.join();
    }

    @Test
    public void testContextHandler() throws Exception {
        Server server = new Server(8080);  // 创建一个 jetty 服务

        ContextHandler contextHandler = new ContextHandler();

        ResourceHandler resourceHandler = new ResourceHandler();

        //配置ResourceHandler，设置哪个文件应该被提供给请求方
        //这个例子里，配置的是当前路径下的文件，但是实际上可以配置长任何jvm能访问到的地方
        resourceHandler.setDirectoriesListed(true);
        resourceHandler.setWelcomeFiles(new String[] { "index.html" });
        resourceHandler.setResourceBase("src/main/web");

        contextHandler.setContextPath("/test");
        contextHandler.setHandler(resourceHandler);

        server.setHandler(contextHandler);

        server.start();
        server.join();
    }

    /**
     * 文件服务器
     */
    @Test
    public void testServlet() {
        Server server = new Server(8080);
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
        context.addServlet(new ServletHolder(new HelloServlet()), "/*");
        context.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")), "/it/*");
        context.addServlet(new ServletHolder(new HelloServlet("Bonjour le monde")), "/fr/*");
        try {
            server.start();
            server.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class HelloServlet extends HttpServlet {

        private String greeting = "Hello World";

        public HelloServlet() {
        }

        public HelloServlet(String greeting) {
            this.greeting = greeting;
        }

        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html");
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWriter().println("<h1>" + greeting + "</h1>");
            response.getWriter().println("session=" + request.getSession(true).getId());
        }
    }

    /**
     * 文件服务器
     */
    @Test
    public void testFileServer() {
        Server server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(8080);
        server.addConnector(connector);
        ResourceHandler resource_handler = new ResourceHandler();
        resource_handler.setDirectoriesListed(true);
        resource_handler.setWelcomeFiles(new String[]{"index.html"});
        resource_handler.setResourceBase(".");
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[]{resource_handler, new DefaultHandler()});
        server.setHandler(handlers);
        try {
            server.start();
            server.join();
            System.out.println("start up !");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过xml文件配置服务器
     */
    @Test
    public void testFileServerXml() throws Exception {
        Resource fileserver_xml = Resource.newSystemResource("jetty/jettyConfig.xml");
        XmlConfiguration configuration = new XmlConfiguration(fileserver_xml);
        Server server = (Server) configuration.configure();
        server.start();
        server.join();

    }

    /**
     * 自己定义handler的服务器。
     * 复杂的请求可以由多个处理器来完成，你可以通过不同的方式把它们结合起来，Jetty有几个 HandlerContainer 接口的实现：
     * <li>HandlerCollection | 一个包含多个处理器的集合，按照顺序依次处理。这在响应请求的同时进行统计和日志记录特别有用。
     * <li>HandlerList | 一个包含多个处理器的集合，按照顺序处理，与HandlerCollection不同的是，
     * 当处理器出现异常或者响应被提交或者 request.isHandled() 方法返回true时，后续将不再被调用。
     * 一般用来匹配不同的主机，用来进行不同的处理。 HandlerWrapper | 一个处理器的基类用来进行切面编程。
     * 例如，一个标准的web应用会由context，session，安全和servlet处理器构成。
     * <li>ContextHandlerCollection | 一个特殊的HandlerCollection，使用完整的URI前缀来选择匹配的ContextHandler对请求进行处理。
     */
    @Test
    public void testSimplestServer() {
        Server server = new Server(8080);
        server.setHandler(new HelloHandler());
        try {
            server.start();
            server.join();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 一个或多个处理器将处理Jetty所有的请求。
     * 一些处理器会转发请求到其他处理器（例如： ContextHandlerCollection 会根据路径选择匹配的ContextHandler），
     * 另一些将根据逻辑判断来生成相应的响应（例如：ServletHandler 将把request转发到servlet），
     * 还有的处理器将处理和请求无关的操作（例如：RequestLogHandler 或者StatisticsHandler）。
     */
    static class HelloHandler extends AbstractHandler {
        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            response.setContentType("text/html;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            baseRequest.setHandled(true);
            response.getWriter().println("<h1>Hello World</h1>");
        }
    }

    /**
     * 在8080端口运行了一个HTTP服务，因为没有处理程序，所以这不是一个有效的server，所有请求将会返回404错误信息。
     */
    @Test
    public void testServer() throws Exception {
        // 创建一个 jetty 服务
        Server server = new Server(8080);
        server.start();
        server.dumpStdErr();
        server.join();
    }
}


